package io.gitee.tziye.core.kv.impl;

import io.gitee.tziye.common.RenaiUtil;
import io.gitee.tziye.common.ValueResult;
import io.gitee.tziye.core.instance.DefaultRenaiClient;
import io.gitee.tziye.core.kv.MapOperator;
import io.gitee.tziye.core.kv.RenaiKey;
import io.gitee.tziye.core.kv.RenaiValue;
import io.gitee.tziye.enums.KvType;
import io.gitee.tziye.jdbc.JdbcProxy;
import io.gitee.tziye.jdbc.ProcedureResult;
import io.vavr.Tuple2;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static io.gitee.tziye.common.Constants.*;

public class DefaultMapOperator extends MapOperator {

    private final KvCommonOperator kvCommonOperator;
    private final JdbcProxy jdbcProxy;

    public DefaultMapOperator(DefaultRenaiClient renaiClient) {
        super(renaiClient);
        kvCommonOperator = new KvCommonOperator(renaiClient);
        jdbcProxy = renaiClient.getJdbcProxy();
    }

    @Override
    public void saveKey(String key, Integer expireSeconds) {
        kvCommonOperator.saveKey(key, expireSeconds, KvType.Map);
    }

    @Override
    public RenaiKey key(String key) {
        return kvCommonOperator.key(key, KvType.Map);
    }

    @Override
    public List<RenaiKey> keys(List<String> keys) {
        return kvCommonOperator.keys(keys, KvType.Map);
    }

    @Override
    public List<RenaiKey> keys() {
        return kvCommonOperator.keys(KvType.Map);
    }

    @Override
    public List<RenaiKey> like(String pattern) {
        return kvCommonOperator.like(pattern, KvType.Map);
    }

    @Override
    public List<RenaiKey> regexp(String pattern) {
        return kvCommonOperator.regexp(pattern, KvType.Map);
    }

    @Override
    public List<RenaiValue> values(String key) {
        return kvCommonOperator.values(key, KvType.Map);
    }

    @Override
    public int total() {
        return kvCommonOperator.total(KvType.Map);
    }

    @Override
    public int size(String key) {
        return kvCommonOperator.size(key, KvType.Map);
    }

    @Override
    public void expire(String key, int expireSeconds) {
        kvCommonOperator.expire(key, expireSeconds, KvType.Map);
    }

    @Override
    public boolean exists(String key) {
        return kvCommonOperator.exists(key, KvType.Map);
    }

    @Override
    public <T> boolean contains(String key, T value) {
        return kvCommonOperator.contains(key, value, KvType.Map);
    }

    @Override
    public int clear(String key) {
        return kvCommonOperator.clear(key, KvType.Map);
    }

    @Override
    public <T> boolean remove(String key, T value) {
        return kvCommonOperator.remove(key, value, KvType.Map);
    }

    @Override
    public <T> int removeValues(String key, Collection<T> values) {
        return kvCommonOperator.removeValues(key, values, KvType.Map);
    }

    @Override
    public boolean delete(String key) {
        return kvCommonOperator.delete(key, KvType.Map);
    }

    @Override
    public int delete(Collection<String> keys) {
        return kvCommonOperator.delete(keys, KvType.Map);
    }

    @Override
    public void flush() {
        kvCommonOperator.flush(KvType.Map);
    }

    @Override
    public <T> Map<String, T> members(String key, Class<T> clazz) {
        assert clazz != null;
        List<RenaiValue> renaiValues = kvCommonOperator.values(key, KvType.Map);
        return renaiValues.stream().collect(Collectors.toMap(RenaiValue::getSubK,
                rv -> RenaiUtil.parse(rv.getV(), clazz), (v1, v2) -> v1));
    }

    @Override
    public <T> void put(String key, String subKey, T value) {
        commonSet(key, List.of(new Tuple2<>(subKey, value)));
    }

    @Override
    public <T> void put(String key, Map<String, T> map) {
        List<Tuple2<String, T>> list = map.entrySet().stream()
                .map(e -> new Tuple2<>(e.getKey(), e.getValue())).toList();
        commonSet(key, list);
    }

    public <T> void commonSet(String key, List<Tuple2<String, T>> list) {
        assert StringUtils.isNotBlank(key) && CollectionUtils.isNotEmpty(list)
                && list.stream().noneMatch(t -> StringUtils.isBlank(t._1) || t._2 == null);
        Map<String, Object> params = procedureParams(key);
        params.put(VALUE, RenaiUtil.string(list));
        jdbcProxy.call(KV_MAP_SET, params);
    }

    @Override
    public <T> T get(String key, String subKey, Class<T> clazz) {
        assert clazz != null;
        ProcedureResult result = commonGet(key, List.of(subKey), 1);
        List<RenaiValue> values = result.list(RenaiValue.class);
        if (CollectionUtils.isEmpty(values)) {
            return null;
        }
        return RenaiUtil.parse(values.get(0).getV(), clazz);
    }

    @Override
    public <T> Map<String, T> get(String key, Collection<String> subKeys, Class<T> clazz) {
        assert clazz != null;
        ProcedureResult result = commonGet(key, subKeys, 1);
        List<RenaiValue> values = result.list(RenaiValue.class);
        return values.stream().collect(Collectors.toMap(RenaiValue::getSubK,
                v -> RenaiUtil.parse(v.getV(), clazz), (v1, v2) -> v1));
    }

    @Override
    public <T> T pop(String key, String subKey, Class<T> clazz) {
        assert clazz != null;
        ProcedureResult result = commonGet(key, List.of(subKey), 2);
        List<RenaiValue> values = result.list(RenaiValue.class);
        if (CollectionUtils.isEmpty(values)) {
            return null;
        }
        return RenaiUtil.parse(values.get(0).getV(), clazz);
    }

    @Override
    public <T> Map<String, T> pop(String key, Collection<String> subKeys, Class<T> clazz) {
        assert clazz != null;
        ProcedureResult result = commonGet(key, subKeys, 2);
        List<RenaiValue> values = result.list(RenaiValue.class);
        return values.stream().collect(Collectors.toMap(RenaiValue::getSubK,
                v -> RenaiUtil.parse(v.getV(), clazz), (v1, v2) -> v1));
    }

    @Override
    public boolean removeKey(String key, String subKey) {
        ProcedureResult result = commonGet(key, List.of(subKey), 3);
        return result.updated() > 0;
    }

    @Override
    public int removeKeys(String key, Collection<String> subKeys) {
        ProcedureResult result = commonGet(key, subKeys, 3);
        return result.updated();
    }

    public ProcedureResult commonGet(String key, Collection<String> subKeys, int flag) {
        assert StringUtils.isNotBlank(key) && CollectionUtils.isNotEmpty(subKeys);
        Map<String, Object> params = procedureParams(key);
        params.put(SUB_KEY, RenaiUtil.string(subKeys));
        params.put(FLAG, flag);
        return jdbcProxy.call(KV_MAP_GET, params);
    }

    @Override
    public boolean containsKey(String key, String subKey) {
        assert StringUtils.isNoneBlank(key, subKey);
        String sql = "SELECT COUNT(id) FROM renai_kv_map_value_valid WHERE k = ? AND sub_k = ?";
        return jdbcProxy.queryForObject(sql, Integer.class, key, subKey) > 0;
    }

    @Override
    public long incr(String key, String subKey, long num) {
        assert StringUtils.isNoneBlank(key, subKey);
        Map<String, Object> params = procedureParams(key);
        params.put(SUB_KEY, subKey);
        params.put(VALUE, num);
        ProcedureResult result = jdbcProxy.call(KV_MAP_INCR, params);
        return result.get(ValueResult.class).getLv();
    }

}
